/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package javaproject;

import java.text.NumberFormat;
import javax.media.j3d.Behavior;
import javax.media.j3d.WakeupOnElapsedFrames;

class FPSCounter extends Behavior {
  // Wakeup condition - framecount = 0 -> wakeup on every frame
  WakeupOnElapsedFrames FPSwakeup = new WakeupOnElapsedFrames(0);

  // Do calibration for these many millisec
  private static final long testduration = 1000;

  // Report frame rate after every sampleduration milliseconds
  private static final long sampleduration = 2000;

  // Flag to indicate that it is time to (re)calibrate
  private boolean doCalibration = true;

  // Flag to indicate the counter has started
  private boolean startup = true;

  // Wait for HotSpot compiler to perform optimizations
  private boolean warmup = true;

  // Time to wait for HotSpot compiler to stabilize (in milliseconds)
  private long warmupTime = 2000;

  // Counter for number of frames rendered
  private int numframes = 0;

  // Report frame rate after maxframe number of frames have been rendered
  private int maxframes = 1;

  // Variables to keep track of elapsed time
  private long startuptime = 0;

  private long currtime = 0;

  private long lasttime = 0;

  private long deltatime;

  // Run indefinitely or for a fixed duration
  private boolean finiteLoop = false;

  // No. of sampling intervals to run for if not running indefinitely
  private long maxLoops;

  // No. of sampling intervals run for so far
  private long numLoops = 0;

  // Total number of frames rendered so far
  private int sumFrames = 0;

  // Total time since last reporting of average frame rate
  private long sumTimes = 0;

  // Counts no. of sampling intervals
  private int loop = 0;

  // Average frame rate is reported after loopCount number of
  // sampling intervals
  private int loopCount = 5;

  private double sumFps = 0.0;

  private String symbol[] = { "\\", "|", "|", "/", "-", "|", "-" };

  int index = 0;

  private NumberFormat nf = null;

  public FPSCounter() {
    setEnable(true);
    nf = NumberFormat.getNumberInstance();
  }

  // Called to init the behavior
  public void initialize() {
    // Set the trigger for the behavior to wakeup on every frame rendered
    wakeupOn(FPSwakeup);
  }

  // Called every time the behavior is activated
  public void processStimulus(java.util.Enumeration critera) {
    // Apply calibration algorithm to determine number of frames to
    // wait before computing frames per second.
    // sampleduration = 10000 -> to run test, pass for 10 seconds.

    if (doCalibration) { // start calibration
      if (startup) {
        // Record time at which the behavior was first invoked
        startuptime = System.currentTimeMillis();
        startup = false;
      } else if (warmup) { // Wait for the system to stabilize.
        System.out.print("\rFPSCounter warming up..."
            + symbol[(index++) % symbol.length]);
        currtime = System.currentTimeMillis();
        deltatime = currtime - startuptime;
        if (deltatime > warmupTime) {
          // Done waiting for warmup
          warmup = false;
          lasttime = System.currentTimeMillis();
          System.out.println("\rFPSCounter warming up...Done");
        }
      } else {
        numframes += 1;
        // Wait till at least maxframe no. of frames have been rendered
        if (numframes >= maxframes) {
          currtime = System.currentTimeMillis();
          deltatime = currtime - lasttime;
          // Do the calibration for testduration no. of millisecs
          if (deltatime > testduration) {
            // Compute total no. of frames rendered so far in the
            // current sampling duration
            maxframes = (int) Math
                .ceil((double) numframes
                    * ((double) sampleduration / (double) deltatime));

            // Done with calibration
            doCalibration = false;
            // reset the value for the measurement
            numframes = 0;
            lasttime = System.currentTimeMillis();
          } else {
            // Need to run the calibration routine for some more
            // time. Increase the no. of frames to be rendered
            maxframes *= 2;
          }
        }
      }
    } else { // do the measurement
      numframes += 1;
      if (numframes >= maxframes) {
        currtime = System.currentTimeMillis();
        deltatime = currtime - lasttime;
        // time is in millisec, so multiply by 1000 to get frames/sec
        double fps = (double) numframes / ((double) deltatime / 1000.0);

        System.out.println("Frame Rate : \n\tNo. of frames : "
            + numframes + "\n\tTime : "
            + ((double) deltatime / 1000.0) + " sec."
            + "\n\tFrames/sec : " + nf.format(fps));

        // Calculate average frame rate
        sumFrames += numframes;
        sumTimes += deltatime;
        sumFps += fps;
        loop++;
        if (loop >= loopCount) {
          double avgFps = (double) sumFrames * 1000.0
              / (double) sumTimes;
          double ravgFps = sumFps / (double) loopCount;
          System.out.println("Aggregate frame rate "
              + nf.format(avgFps) + " frames/sec");
          System.out.println("Average frame rate "
              + nf.format(ravgFps) + " frames/sec");
          numLoops++;
          if (finiteLoop && numLoops >= maxLoops) {
            System.out
                .println("************** The End **************\n");
            setEnable(false);
          }
          loop = 0;
          sumFps = 0;
        }
        numframes = 0;
        lasttime = System.currentTimeMillis();
      }
    }
    // Set the trigger for the behavior
    wakeupOn(FPSwakeup);
  }

  /**
   * The frame counter waits for some time before computing the frame rate.
   * This allows the HotSpot compiler to perform initial optimizations. The
   * amount of time to wait for is set by this method. The default is 20000
   * (20 sec)
   *
   * @param Amount
   *            of time to wait for before computing frame rate (specified in
   *            milliseconds)
   */
  public void setWarmupTime(long wt) {
    warmupTime = wt;
  }

  /**
   * Sets the number of sampling intervals to wait for before computing the
   * average frame rate. The default is 5.
   *
   * @param No.
   *            of sampling intervals over which to compute frame rate. A
   *            value of 0 implies the average frame rate is computed over one
   *            sampling interval
   */
  public void setLoopCount(int lc) {
    loopCount = lc;
  }

  /**
   * This method sets the number of sampling intervals for which the frame
   * counter should run.
   *
   * @param No.
   *            of sampling intervals to run for
   */
  public void setMaxLoops(int ml) {
    maxLoops = ml;
    finiteLoop = true;
  }

}


